<?php

namespace App\Http\Controllers\V1\API;

use App\Models\V1\ApplyPays;
use App\Models\V1\NewStudent;
use App\Models\V1\Pays;
use App\Models\V1\WeChatLogin;
use GuzzleHttp\Client;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\DB;
use Log;
use Validator;
class WeChatPayController extends BaseController
{
    protected $app_id = 'wx6397396df988f501';
    protected $app_secret = 'f767a74b8fa834acbdf55971e858cb81';
    protected $we_uri = 'https://api.mch.weixin.qq.com/pay/unifiedorder';

    //商户号
    protected $mch_id = '1494251122';
    protected $api_key = 'DkXtYvYK6Tv3I3Zn2JFpVqB7xLgG6ro0';  //支付秘钥

    //统一下单,生成预支付订单
    public function unifiedOrder(Request $request) {
        $result = [
            'errorCode' => Response::HTTP_NOT_FOUND,
            'errorMsg' => Response::$statusTexts[Response::HTTP_NOT_FOUND],
        ];
        DB::beginTransaction();
        try {
            $error = $this->validateApply($request, $result);
            if (!empty($error) || isset($error)) {
                $result = $error;
                return $this->sendResponse($result, $result['errorCode']);
            }
            $exam_code = $request->get('userID');
            $pay_codes = $request->get('payIDs');

            $openid = $this->getOpenID($exam_code);
            if (empty($openid) || !isset($openid)) {
                $result['errorCode'] = Response::HTTP_BAD_REQUEST;
                $result['errorMsg'] = '用户未与微信关联';
                return $this->sendResponse($result, $result['errorCode']);
            }
            $appid = $this->app_id;
            $mch_id = $this->mch_id;
            $nonce_str = $this->getNonceStr();
            $body = 'XXX学校收费－'.$this->getBody($pay_codes,$exam_code);
            $out_trade_no = $this->getTradeNO();  // 订单号
            Log::info('$out_trade_no ==== '.$out_trade_no);
//            $total_fee = 1; //总金额
            $total_fee = $this->getTotal($pay_codes,$exam_code); //总金额
            Log::info('total = '.$total_fee);
            $spbill_create_ip = $request->getClientIp();  //终端ip
            $notify_url = "https://".$_SERVER['HTTP_HOST'].'/api/v1/wechat/pay/notify';
            $trade_type = "JSAPI";
            $sign_type = 'MD5';

            //统一下单参数构造
            $data = [
                'appid'=>$appid,
                'body'=>$body,
                'mch_id'=>$mch_id,
                'nonce_str'=>$nonce_str,
                'notify_url'=>$notify_url,
                'openid'=>$openid,
                'out_trade_no'=>$out_trade_no,
                'sign_type' => $sign_type,
                'spbill_create_ip'=>$spbill_create_ip,
                'total_fee'=>$total_fee,
                'trade_type'=>$trade_type,
            ];
            $data['sign'] = $this->getSign($data);
            Log::info('data',$data);
            //转化成xml格式
            $xmldata = $this->arrayToxml($data);
            Log::info('$xmldata',['$xmldata'=>$xmldata]);
            $dir = dirname(__FILE__);
//            $client = new Client(['verify' => '/etc/letsencrypt/live/wechat.yejinsoft.com/fullchain.pem']);
            $client = new Client(['verify' => $dir . '/DigiCert_Global_Root_CA.pem']);
//            $client = new Client(['verify' => $dir . '/cacert.pem']);

            //调统一下单接口
            $response = $client->post($this->we_uri,[
                'body' => $xmldata
            ]);
            $order_result = $response->getBody()->getContents();
            $order_result = $this->xmlToArray($order_result);
            Log::info('$order_result',$order_result);
            $return_code = isset($order_result['return_code'])?$order_result['return_code']:'';
            $result_code = isset($order_result['result_code'])?$order_result['result_code']:'';

////            //预支付订单id
//            $prepay_id = '678900987654';
//            //将数据包再次签名
//            $orderInfo = $this->paySign($prepay_id);
//
//            //插入到数据库
//            $apply_pay = [
//                'exam_code' => $exam_code,
//                'pay_code' => json_encode($pay_codes),
//                'amount' => $total_fee*0.01,
//                'out_trade_no' => $out_trade_no,
//                'status' => false,
//                'type' => 2,
//                'arrival_code' => $this->getArrivalCode($exam_code)
//            ];
//            ApplyPays::create($apply_pay);
//            DB::commit();
//            $result['data'] = $orderInfo;
//            $result['errorCode'] = Response::HTTP_OK;
//            $result['errorMsg'] = '成功';
//            return $this->sendResponse($result, $result['errorCode']);

            if (strval($return_code) === 'FAIL') {
                $result['errorCode'] = Response::HTTP_BAD_REQUEST;
                $result['errorMsg'] = $order_result['return_msg'];
                return $this->sendResponse($result, $result['errorCode']);
            }
            if (strval($result_code) === 'FAIL') {
                $result['errorCode'] = Response::HTTP_BAD_REQUEST;
                $result['errorMsg'] = $order_result['err_code_des'];
                return $this->sendResponse($result, $result['errorCode']);
            }
            if (strval($return_code) === 'SUCCESS' && strval($result_code) === 'SUCCESS') {
                //预支付订单id
                $prepay_id = $order_result['prepay_id'];
                //将数据包再次签名
                $orderInfo = $this->paySign($prepay_id);

                //插入到数据库
                $apply_pay = [
                    'exam_code' => $exam_code,
                    'pay_code' => json_encode($pay_codes),
                    'amount' => $total_fee*0.01,
                    'out_trade_no' => $out_trade_no,
                    'status' => false,
                    'type' => 2,
                    'arrival_code' => $this->getArrivalCode($exam_code)
                ];
                ApplyPays::create($apply_pay);
                DB::commit();
                $result['data'] = $orderInfo;
                $result['errorCode'] = Response::HTTP_OK;
                $result['errorMsg'] = '成功';
                return $this->sendResponse($result, $result['errorCode']);
            }else {
                $result['errorCode'] = Response::HTTP_BAD_REQUEST;
                $result['errorMsg'] = '获取有误';
                return $this->sendResponse($result, $result['errorCode']);
            }

        }catch (\Exception $e) {
            DB::rollback();
            $result['errorCode'] = Response::HTTP_BAD_REQUEST;
            $result['errorMsg'] = $e->getMessage();
            return $this->sendResponse($result, $result['errorCode']);
        }
    }

    //获取post数据
    private function post_data(){
        $receipt = $_REQUEST;
        if($receipt==null){
            $receipt = file_get_contents("php://input");
            if($receipt == null){
                $receipt = $GLOBALS['HTTP_RAW_POST_DATA'];
            }
        }
        return $receipt;
    }

    /***
     * 支付后微信回调
     */
    public function orderNotify() {
        //获取数据
        $xml = $this->post_data();
        //将服务器返回的XML数据转化为数组
        $data = $this->xmlToArray($xml);
        // 保存微信服务器返回的签名sign
        $data_sign = $data['sign'];
        // sign不参与签名算法
        unset($data['sign']);
        $user_sign = $this->getSign($data);
        $return_code = isset($data['return_code'])?$data['return_code']:'';
        $return_msg = isset($data['return_msg'])?$data['return_msg']:'';
        //表示通信成功
        if ($return_code === 'SUCCESS') {
            $result_code = isset($data['result_code'])?$data['result_code']:'';
            //表示业务结果成功
            if ($result_code === 'SUCCESS') {
                //判断签名是否正确
                if ($user_sign===$data_sign) {
                    $out_trade_no = $data['out_trade_no'];
                    if (!empty($out_trade_no)) {
                        try {
                            $apply_pay = ApplyPays::where(['out_trade_no'=>$out_trade_no,'type'=>2])->first();
                            if (!empty($apply_pay)) {
                                //回调金额
                                $total_fee = $data['total_fee'];
                                //数据库中金额
                                $amount = $apply_pay['amount'];
                                if ($total_fee == $amount*100) {
                                    $status = $apply_pay->status;
                                    //数据库支付状态已是成功
                                    if ($status == false) {
                                        $transaction_id = $data['transaction_id'];
                                        $update_data = [
                                            'transaction_id'=>$transaction_id,
                                            'status'=>true
                                        ];
                                        $apply_pay->update($update_data);
                                    }
                                    $return_code = 'SUCCESS';
                                    $return_msg = 'ok';
                                }else {
                                    $return_code = 'FAIL';
                                    $return_msg = '金额不匹配';
                                }
                            }else {
                                $return_code = 'FAIL';
                                $return_msg = '商户系统中不存在该订单';
                            }
                        }catch (\Exception $e) {
                            ////系统异常
                            $return_code = 'FAIL';
                            $return_msg = $e->getMessage();
                        }
                    }else{
                        //支付结果中商户订单号不存在
                        $return_code = "FAIL";
                        $return_msg = "支付结果中商户订单号不存在";
                    }
                }else{
                    $return_code = "FAIL";
                    $return_msg = "签名失败";
                }
            }else{
                $return_code = "FAIL";
                $return_msg = "交易失败";
            }
        }else{
            $return_code = "FAIL";
            $return_msg = "交易失败";
        }

        $str = '<xml>
        <return_code><![CDATA['.$return_code.']]></return_code>
        <return_msg><![CDATA['.$return_msg.']]></return_msg>
        </xml>';
        return $str;
    }


    //查询订单
    public function orderQuery(Request $request) {
        $transaction_id = $request->get('transaction_id');
        $out_trade_no = $request->get('out_trade_no');
        $exam_code = $request->get('userID');
        //统一下单参数构造
        $data = [
            'appid'=>$this->app_id,
            'mch_id'=>$this->mch_id,
            'transaction_id'=>$transaction_id,
            'out_trade_no'=>$out_trade_no,
            'nonce_str'=>$this->getNonceStr(),
        ];
        $data['sign'] = $this->getSign($data);
        //转化成xml格式
        $xmldata = $this->arrayToxml($data);
        $dir = dirname(__FILE__);
//        $client = new Client(['verify' => '/etc/letsencrypt/live/wechat.yejinsoft.com/fullchain.pem']);
        $client = new Client(['verify' => $dir . '/DigiCert_Global_Root_CA.pem']);
//        $client = new Client(['verify' => $dir . '/cacert.pem']);
        $query_uri = 'https://api.mch.weixin.qq.com/pay/orderquery';
        //调统一下单接口
        $response = $client->post($query_uri,[
            'body' => $xmldata
        ]);
        $order_result = $response->getBody()->getContents();
        if(empty($order_result)) {
            $result['errorCode'] = Response::HTTP_BAD_REQUEST;
            $result['errorMsg'] = '调统一下单返回数据异常';
            return $this->sendResponse($result, $result['errorCode']);
        }
        $order_result = $this->xmlToArray($order_result);
        $return_code = isset($order_result['return_code'])?$order_result['return_code']:'';
        $result_code = isset($order_result['result_code'])?$order_result['result_code']:'';

        if (strval($return_code) === 'FAIL') {
            $result['errorCode'] = Response::HTTP_BAD_REQUEST;
            $result['errorMsg'] = $order_result['return_msg'];
            return $this->sendResponse($result, $result['errorCode']);
        }
        if (strval($result_code) === 'FAIL') {
            $result['errorCode'] = Response::HTTP_BAD_REQUEST;
            $result['errorMsg'] = $order_result['err_code_des'];
            return $this->sendResponse($result, $result['errorCode']);
        }
        if (strval($return_code) === 'SUCCESS' && strval($result_code) === 'SUCCESS') {
            //交易状态
            $trade_state = $order_result['trade_state'];
//            $total_fee = $order_result['total_fee'];
            $trade_state_desc = $order_result['trade_state_desc'];
            if ($trade_state === 'SUCCESS') {
                $update_data = [
                    'status' => true
                ];
                $rspObj = ApplyPays::where(['exam_code'=>$exam_code,'out_trade_no'=>$out_trade_no])->update($update_data);
                if ($rspObj){
                    $result['errorCode'] = Response::HTTP_OK;
                    $result['errorMsg'] = $trade_state_desc;
                }else {
                    $result['errorCode'] = Response::HTTP_BAD_REQUEST;
                    $result['errorMsg'] = '失败';
                }
            }else {
                $result['errorCode'] = Response::HTTP_OK;
                $result['errorMsg'] = $trade_state_desc;
            }
            return $this->sendResponse($result, $result['errorCode']);
        }
    }


    /***
     * 验证生成预支付订单参数
     * @param Request $request
     */
    private function validateApply(Request $request) {
        //校验规则
        $rules = array();
        $rules['userID'] = 'required|exists:basic_new_students,exam_code,deleted_at,NULL|bail';
        $rules['payIDs'] = 'required';

        //错误提示信息
        $messages = array();
        $messages['userID.required'] = '用户代码不能为空';
        $messages['userID.exists'] = '用户不存在';
        $messages['payIDs.required'] = '支付项不能为空';

        $validator = Validator::make($request->all(),$rules,$messages);  //第一个参数是提交的数据，第二个是校对规则，第三个是错误信息
        $errors = $validator->errors()->all(); //这是捕获到的所有参数验证的错误信息
        if(!empty($errors)) {
            $first_errors = $errors[0];
            $result['errorCode'] = Response::HTTP_BAD_REQUEST;
            $result['errorMsg'] = $first_errors;
            return $result;
        }
    }


    /***
     * 获取支付总金额
     * @param $pay_codes
     * @param $exam_code
     * @return int
     */
    private function getTotal($pay_codes,$exam_code) {
        $arrival_code = $this->getArrivalCode($exam_code);
        $total = 0;
        foreach ($pay_codes as $code) {
            $pay = Pays::where(['code'=>$code,'arrival_code'=>$arrival_code])->first();
             $total += $pay['amount'];
        }
        return $total*100;
    }


    /****
     * 获取商品描述
     * @param $pay_codes
     * @param $exam_code
     * @return int
     */
    private function getBody($pay_codes,$exam_code) {
        $arrival_code = $this->getArrivalCode($exam_code);
        $body = '';
        foreach ($pay_codes as $code) {
            $pay = Pays::where(['code'=>$code,'arrival_code'=>$arrival_code])->first();
            if (empty($body)) {
                $body .= $pay['name'];
            }else{
                $body .= ',' . $pay['name'];
            }
        }
        return $body;
    }
    /**
     *
     * 产生随机字符串，不长于32位
     * @param int $length
     * @return 产生的随机字符串
     */
    private function getNonceStr($length = 32) {
        $chars = "0123456789abcdefghijklmnopqrstuvwxyz";
        $str ="";
        for ( $i = 0; $i < $length; $i++ )  {
            $str .= substr($chars, mt_rand(0, strlen($chars)-1), 1);
        }
        Log::info("NonceStr = ".$str);
        return $str;
    }

    //获得签名
    private function getSign($data) {
        //获取微信支付秘钥
        $key = $this->api_key;
        // 去空
        $data = array_filter($data);
        //签名步骤一：按字典序排序参数
        ksort($data);
        //转成key-value的字符串
        $string_a = http_build_query($data);
        Log::info('$string_a1 = '.$string_a);
        $string_a = urldecode($string_a);
        Log::info('$string_a2 = '.$string_a);
        //签名步骤二：在string后加入KEY支付秘钥
        $string_sign_temp = $string_a."&key=".$key;
        //签名步骤三：MD5加密
        $sign = md5($string_sign_temp);
        // 签名步骤四：所有字符转为大写
        $result = strtoupper($sign);
        return $result;
    }

    /****
     * 生成订单单号
     * 根据当前系统时间加随机序列来生成订单号
     * @return string
     */
    private function getTradeNO() {
        list($usec, $sec) = explode(" ", microtime());
        $usec = substr(str_replace('0.', '', $usec), 0 ,4);
        $str  = rand(10,99);
        Log::info('$usec = '.$usec);
        Log::info('trade_no = '.$str);
        return date("YmdHis").$usec.$str;
    }

    /****
     * 获取openid
     * @return string
     */
    private function getOpenID($code) {
        $user = WeChatLogin::where('exam_code',$code)->first();
        if (!empty($user)) {
            return $user->openid;
        }
        return '';
    }

    /**
     * 将一个数组转换为 XML 结构的字符串
     * @param array $arr 要转换的数组
     * @param int $level 节点层级, 1 为 Root.
     * @return string XML 结构的字符串
     */
    private function arrayToxml($arr, $level = 1) {
        $s = $level == 1 ? "<xml>" : '';
        foreach($arr as $tag_name => $value) {
            if (is_numeric($tag_name)) {
                $tag_name = $value['TagName'];
                unset($value['TagName']);
            }
            if(!is_array($value)) {
                $s .= "<{$tag_name}>".(!is_numeric($value) ? '<![CDATA[' : '').$value.(!is_numeric($value) ? ']]>' : '')."</{$tag_name}>";
            } else {
                $s .= "<{$tag_name}>" . $this->array2xml($value, $level + 1)."</{$tag_name}>";
            }
        }
        $s = preg_replace("/([\x01-\x08\x0b-\x0c\x0e-\x1f])+/", ' ', $s);
        return $level == 1 ? $s."</xml>" : $s;
    }

    /**
     * 将xml转为array
     * @param  string     $xml xml字符串
     * @return array    转换得到的数组
     */
    protected function xmlToArray($xml){
        //禁止引用外部xml实体
        libxml_disable_entity_loader(true);
        $result= json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
        return $result;
    }


    /***
     * 返回prepay_id后二次签名
     * 支付接口签名
     * @param $prepay_id
     */
    private function paySign($prepay_id) {
        $data = array(
            'appId' => $this->app_id, //小程序ID
            'timeStamp' => '' . time() . '', //时间戳
            'nonceStr' => $this->getNonceStr(32), //随机串
            'package' => 'prepay_id=' . $prepay_id, //数据包
            'signType' => 'MD5'//签名方式
        );
        $data['paySign'] = $this->getSign($data);
        return $data;
    }

    /***
     * 获取当前的迎新代码
     * @return array
     */
    private function getArrivalCode($code)
    {
        $student = NewStudent::where('exam_code',$code)->first();
        return $student['arrival_code'];
    }


    /***
     * 微信验证证书接口
     * @return string
     */
    public function test() {
        $data = [
            'mch_id'=>$this->mch_id,
            'nonce_str'=>$this->getNonceStr(),
        ];
        $data['sign'] = $this->getSign($data);
        //转化成xml格式
        $xmldata = $this->arrayToxml($data);
        $dir = dirname(__FILE__);
//        $client = new Client(['verify' => $dir . '/DigiCert_Global_Root_CA.pem']);
        $client = new Client(['verify' => $dir . '/wechat.pem']);
        $query_uri = 'https://apitest.mch.weixin.qq.com/sandboxnew/pay/getsignkey';
        //调统一下单接口
        $response = $client->post($query_uri,[
            'body' => $xmldata
        ]);
        $order_result = $response->getBody()->getContents();
        return $order_result;
    }
}
